package com.zdp.oauth.config;

import com.zdp.oauth.service.OauthUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.annotation.Resource;
import java.util.Arrays;


/**
 * <h1>授权服务配置</h1>
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * <h2>1. 用来配置客户端详情服务（ClientDetailsService），客户端详情信息在
     * 这里进行初始化，你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息</h2>
     *
     * @apiNote
     * 配置谁能来访问授权中心申请令牌的问题，配置可支持的客户端
     * */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 客户端信息可以配置在数据库里面，这里为了测试配置在内存中
        clients.inMemory() // 使用in‐memory存储
                .withClient("c1") // client_id
                .secret(passwordEncoder.encode("secret")) //客户端密钥
                // 将来客户端要拿着client_id和secret来申请令牌
                .resourceIds("res1") // 配置的就是资源id，配合资源服务使用
                .authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token") // 获取令牌的方式
                // 该client允许的授权类型 authorization_code,password,refresh_token,implicit,client_credentials
                .scopes("all") // 允许的授权范围 read或者一个服务id order-server只是一个标识而已
                .autoApprove(false) //false authorization_code模式会跳转到授权的页面让用户授权 true就是直接发令牌
                .redirectUris("http://www.baidu.com"); //加上验证回调地址
    }

    // jwt密钥
    private String SIGNING_KEY = "uaa123";

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public TokenEnhancer customTokenEnhancer() {
        return new CustomTokenEnhancer();
    }

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Bean
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service=new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService); //客户端详情服务
        service.setSupportRefreshToken(true); //是否刷新令牌
        service.setTokenStore(tokenStore()); //令牌的存储策略
        service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
        //设置token增强 - 设置jwt-token转换器
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), jwtAccessTokenConverter()));
        service.setTokenEnhancer(tokenEnhancerChain);  //jwtAccessTokenConverter()

        return service;
    }

    // oauth2.0授权模式为授权码模式时必须配置AuthenticationManager
    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;
    // oauth2.0授权模式为密码模式时必须配置AuthenticationManager
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private OauthUserService oauthUserService;

    /**
     *<h2>2. 用来配置令牌（token）的访问端点和令牌服务(token services)</h2>
     *
     * @apiNote
     * 用来解决申请到的令牌怎么存储的以及token的格式,管理token
     * */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager) // 密码模式需要
                .authorizationCodeServices(authorizationCodeServices) // 授权码模式需要
                .userDetailsService(oauthUserService)// 可配置可不配置(通过用户名获取用户授权的基本信息)
                .tokenServices(tokenService()) // 令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST); // 允许POST请求
    }

    /**
     * <h2>用来配置令牌端点的安全约束</h2>
     *
     * @apiNote
     * 表示并不是所有请求都可以访问到授权服务
     * */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // tokenKey这个endpoint当使用JwtToken且使用非对称加密时，资源服务用于获取公钥而开放的，这里指这个endpoint完全公开。
                .tokenKeyAccess("permitAll()") // /oauth/token_key：提供公有密匙的端点，如果你使用JWT令牌的话。打开
                // checkToken这个endpoint完全公开
                .checkTokenAccess("permitAll()") /// oauth/check_token：用于资源服务访问的令牌解析端点。打开
                // 允许表单认证
                .allowFormAuthenticationForClients()
        ;
    }


}
